﻿//
//  Copyright (c) 2009,2010 Liang Quan Qing.
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions
//  are met:
//  1. Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//
//  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
//  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
//  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
//  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
//  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
//  SUCH DAMAGE.
//

static int Null_init (fcnv_t* pfcnv)
{
    pfcnv->error = FCNV_ERR_NOT_SUPPORTED;
    return -1;
}

static int Null_ToUTF16(fcnv_t* pfcnv, const char* srcString, size_t srcSizeInBytes, WChar_t * pBuffer, size_t destSizeInWChars)
{
    pfcnv->error = FCNV_ERR_NOT_SUPPORTED;
    return -1;
}

static int Null_FromUTF16(fcnv_t* pfcnv, const WChar_t* srcString, size_t srcSizeInWChars, char * pBuffer, size_t destSizeInBytes)
{
    pfcnv->error = FCNV_ERR_NOT_SUPPORTED;
    return -1;
}

static int Null_ToUTF8(fcnv_t* pfcnv, const char* srcString, size_t srcSizeInBytes, char * pBuffer, size_t destSizeInBytes)
{
    pfcnv->error = FCNV_ERR_NOT_SUPPORTED;
    return -1;
}

static int Null_FromUTF8(fcnv_t* pfcnv, const char* srcString, size_t srcSizeInBytes, char * pBuffer, size_t destSizeInBytes)
{
    pfcnv->error = FCNV_ERR_NOT_SUPPORTED;
    return -1;
}

static int Null_ToUTF32(fcnv_t* pfcnv, const char* srcString, size_t srcSizeInBytes, DChar_t * pBuffer, size_t destSizeInDChars)
{
    pfcnv->error = FCNV_ERR_NOT_SUPPORTED;
    return -1;
}

static int Null_FromUTF32(fcnv_t* pfcnv, const DChar_t*srcString, size_t srcSizeInDChars, char * pBuffer, size_t destSizeInBytes)
{
    pfcnv->error = FCNV_ERR_NOT_SUPPORTED;
    return -1;
}

static int Null_mbtowc(fcnv_t* pfcnv, unsigned int * pdchar, const unsigned char* pmbchar, size_t count)
{
    pfcnv->error = FCNV_ERR_NOT_SUPPORTED;
    return -1;
}

static int Null_wctomb(fcnv_t* pfcnv, unsigned char *pmbchar, unsigned int dchar)
{
    pfcnv->error = FCNV_ERR_NOT_SUPPORTED;
    return -1;
}

static int sbcs_getFirstCharLength(const char *s)
{
    return 1;
}

static int sbcs_isLeadByte(const char *s)
{
    return 0;
}


static int sbcs_t32( fcnv_t*     pfcnv,
                     const char* srcString,
                     size_t      srcSizeInBytes,
                     DChar_t *   pBuffer,
                     size_t      destSizeInDChars)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  count;                                      // the counter
    WChar_t wchar;
    const unsigned char* pSrc;

    pSrc = (const unsigned char*)srcString;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInBytes) ? (srcSizeInBytes) : ( strlen(pSrc) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( pSrc == NULL )                                 // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pBuffer == NULL )                              // pBuffer==NULL, donot convert, calculate target size only
        destSizeInDChars = (size_t)(-1);                //      target size will save into pfcnv->outCount

    for ( count = 0 ; count < inLength; count++ )
    {
        if ( count < destSizeInDChars )
        {
            wchar = __btow(pd, pSrc[count]);
            if ( wchar != 0xffff )
            {
                if ( pBuffer )
                    pBuffer[count] = (DChar_t) wchar;
                pfcnv->inCount ++ ;
                pfcnv->outCount ++ ;
                continue;
            }
            else
            {
                if ( pd->boUseDefaultChar )
                {
                    if ( pBuffer )
                        pBuffer[count] = (DChar_t) pd->DefaultWChar;
                    pfcnv->inCount ++ ;
                    pfcnv->outCount++;
                    continue;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }
    return 0;
}

static int sbcs_f32( fcnv_t*     pfcnv,
                     const DChar_t*srcString,
                     size_t      srcSizeInDChars,
                     char *      pBuffer,
                     size_t      destSizeInBytes)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  count,dcount;
    int ok;                                             // mean convert OK
    WChar_t wChar;
    unsigned char* pDest;

    pDest = (unsigned char*)pBuffer;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInDChars) ? (srcSizeInDChars) : ( dstrlen(srcString) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( srcString == NULL )                            // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pDest == NULL )                                // pBuffer==NULL, donot convert, calculate target size only
        destSizeInBytes = (size_t)(-1);                 //      target size will save into pfcnv->outCount

    count = 0;
    for ( dcount = 0 ; dcount < inLength; )
    {
        if ( count < destSizeInBytes )
        {
            if ( srcString[dcount] < 0xffff )
            {
                wChar = __wtob(pd, (unsigned short) srcString[dcount]);
                if ( wChar != 0xffff )
                    ok = 1;
                else
                    ok = 0;
            }
            else
                ok = 0;
            ///////////
            if ( ok )
            {
                if ( pDest )
                    pDest[count] = (unsigned char) wChar;
                pfcnv->inCount ++;
                dcount ++;
                pfcnv->outCount++;
                count++;
                continue;
            }
            else
            {
                if ( pd->boUseDefaultChar )
                {
                    if ( pDest )
                        pDest[count] = pd->pDefaultChar[0];
                    pfcnv->inCount ++;
                    dcount ++;
                    pfcnv->outCount++;
                    count++;
                    continue;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }

    return 0;
}

static int sbcs_t16(fcnv_t*     pfcnv,
                    const char* srcString,
                    size_t      srcSizeInBytes,
                    WChar_t *   pBuffer,
                    size_t      destSizeInWChars)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  count;                                      // the counter
    WChar_t wchar;
    const unsigned char* pSrc;

    pSrc = (const unsigned char*)srcString;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInBytes) ? (srcSizeInBytes) : ( strlen(pSrc) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( pSrc == NULL )                                 // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pBuffer == NULL )                              // pBuffer==NULL, donot convert, calculate target size only
        destSizeInWChars = (size_t)(-1);                //      target size will save into pfcnv->outCount

    for ( count = 0 ; count < inLength; count++ )
    {
        if ( count < destSizeInWChars )
        {
            wchar = __btow(pd, pSrc[count]);
            if ( wchar != 0xffff )
            {
                if ( pBuffer )
                    pBuffer[count] = wchar;
                pfcnv->inCount ++ ;
                pfcnv->outCount ++ ;
                continue;
            }
            else
            {
                if ( pd->boUseDefaultChar )
                {
                    if ( pBuffer )
                        pBuffer[count] = pd->DefaultWChar;
                    pfcnv->inCount ++ ;
                    pfcnv->outCount++;
                    continue;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }
    return 0;
}

static int sbcs_f16( fcnv_t*     pfcnv,
                     const WChar_t* srcString,
                     size_t      srcSizeInWChars,
                     char *      pBuffer,
                     size_t      destSizeInBytes)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  count,wcount;
    int valid;                                          // mean the UTF-16 surrogate-character is valid
    int utf16width;                                     // the UTF-16 character width
    int ok;                                             // mean convert OK
    WChar_t wChar;
    unsigned char* pDest;


    pDest = (unsigned char*)pBuffer;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInWChars) ? (srcSizeInWChars) : ( wstrlen(srcString) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( srcString == NULL )                            // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pDest == NULL )                                // pBuffer==NULL, donot convert, calculate target size only
        destSizeInBytes = (size_t)(-1);                 //      target size will save into pfcnv->outCount

    count = 0;
    for ( wcount = 0 ; wcount < inLength; )
    {
        if ( count < destSizeInBytes )
        {
            if ( ! isSurrogate(srcString[wcount]) )     // if is normal UTF-16 character, not surrogate
            {
                wChar = __wtob(pd, srcString[wcount]);
                if ( wChar != 0xffff )
                    ok = 1;
                else
                    ok = 0;
                utf16width = 1;
            }
            else
            {
                MixSurrogate(srcString[wcount], srcString[wcount+1], &valid);
                if ( valid )
                    utf16width = 2;
                else
                    utf16width = 1;
                ok = 0;
            }
            ///////////
            if ( ok )
            {
                if ( pDest )
                    pDest[count] = (unsigned char) wChar;
                pfcnv->inCount += utf16width ;
                wcount += utf16width;
                pfcnv->outCount++;
                count++;
                continue;
            }
            else
            {
                if ( pd->boUseDefaultChar )
                {
                    if ( pDest )
                        pDest[count] = pd->pDefaultChar[0];
                    pfcnv->inCount += utf16width ;
                    wcount += utf16width;
                    pfcnv->outCount++;
                    count++;
                    continue;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }

    return 0;
}

static int sbcs_t8( fcnv_t*     pfcnv,
                    const char* srcString,
                    size_t      srcSizeInBytes,
                    char *      pBuffer,
                    size_t      destSizeInBytes)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  count, u8count;                             // the counter
    WChar_t wchar;
    const unsigned char* pSrc;
    unsigned char* pDest;
    int utf8len, i;
    int valid;
    unsigned char utf8buf[8];

    pSrc = (const unsigned char*)srcString;
    pDest = (unsigned char*) pBuffer;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInBytes) ? (srcSizeInBytes) : ( strlen(pSrc) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( pSrc == NULL )                                 // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pDest == NULL )                                // pBuffer==NULL, donot convert, calculate target size only
        destSizeInBytes = (size_t)(-1);                 //      target size will save into pfcnv->outCount

    count = 0;
    u8count = 0;
    while ( count < inLength )
    {
        wchar = __btow(pd, pSrc[count++]);
        if ( wchar != 0xffff )
            U32CharToU8Buff_1(wchar, utf8buf, &utf8len, &valid);
        else
        {
            if ( pd->boUseDefaultChar )
                U32CharToU8Buff_1(pd->DefaultWChar, utf8buf, &utf8len, &valid);
            else
            {
                valid = 0;
                utf8len = 0;
            }
        }
        ////////
        if ( u8count + utf8len < destSizeInBytes )
        {
            if ( valid )
            {
                if ( pDest )
                {
                    for ( i = 0; i < utf8len; i++ )
                        pDest[u8count+i] = utf8buf[i];
                }
                pfcnv->inCount ++ ;
                pfcnv->outCount += utf8len ;
                u8count += utf8len;
                continue;
            }
            else
            {
                pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                return -1;
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }
    return 0;
}

static int sbcs_f8( fcnv_t*     pfcnv,
                    const char* srcString,
                    size_t      srcSizeInBytes,
                    char *      pBuffer,
                    size_t      destSizeInBytes)
{
    priv_t *pd;
    size_t  inLength;
    size_t  count,ucount;
    DChar_t dwChar;
    unsigned char* pDest;
    const unsigned char* pSrc;
    int utf8len;
    int valid, ok;
    unsigned short wChar;

    pDest = (unsigned char*)pBuffer;
    pSrc = (const unsigned char*)srcString;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInBytes) ? (srcSizeInBytes) : ( strlen(pSrc) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    count = 0;
    ucount = 0;
    while ( ucount < inLength )
    {
        if ( count < destSizeInBytes )
        {
            U8BuffToU32Char_1(&pSrc[ucount], &utf8len, &dwChar, &valid);
            ok = 0;
            if ( valid && dwChar < 0xffff )
            {
                wChar = __wtob(pd, (unsigned short)dwChar);
                if ( wChar != 0xffff )
                    ok = 1;
            }
            /////
            if ( ok )
            {
                if ( pDest )
                    pDest[count] = (unsigned char) wChar;
                pfcnv->inCount += utf8len ;
                ucount += utf8len;
                pfcnv->outCount++;
                count++;
            }
            else
            {
                if (pd->boUseDefaultChar)
                {
                    if ( pDest )
                        pDest[count] = pd->pDefaultChar[0];
                    pfcnv->inCount += utf8len ;
                    ucount += utf8len;
                    pfcnv->outCount++;
                    count++;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }
    return 0;
}

// If mbchar is not NULL and if the object that mbchar points to forms a valid multibyte character,
// mbtowc returns the length in bytes of the multibyte character.
// If mbchar is NULL or the object that it points to is a wide-character null character (L'\0'), the function returns 0.
// If the object that mbchar points to does not form a valid multibyte character within the first count characters, it returns –1.

static int sbcs_mbtowcFunc(fcnv_t* pfcnv, unsigned int * pdchar, const unsigned char* pmbchar, size_t count)
{
    priv_t *pd;
    unsigned short wc;

    if ( pmbchar == NULL ) {
        if ( pdchar )
            *pdchar = 0;
        return 0;
    }

    pd = (priv_t*) pfcnv->privdata;
    wc = __btow(pd, (unsigned short) (*pmbchar));
    if ( wc != 0xffff )
    {
        if ( pdchar )
            *pdchar = (unsigned int) wc;
        if ( wc )
            return 1;
        else
            return 0;
    }
    else
        return -1;
}

// If wctomb converts the wide character to a multibyte character, it returns the number of bytes (which is never greater than MB_CUR_MAX) in the wide character.
// If wchar is the wide-character null character (L'\0'), wctomb returns 1. If the target pointer mbchar is NULL, wctomb returns 0.
// If the conversion is not possible in the current locale, wctomb returns –1 and errno is set to EILSEQ.

static int sbcs_wctombFunc(fcnv_t* pfcnv, unsigned char *pmbchar, unsigned int dchar)
{
    priv_t *pd;
    unsigned short c;

    pd = (priv_t*) pfcnv->privdata;

    if (pmbchar == NULL)
        return 0;

    if ( dchar < 0x10000 )
    {
        c = __wtob(pd, (unsigned short)dchar);
        if ( c != 0xffff )
        {
            *pmbchar = (unsigned char) c;
            return 1;
        }
        else
            return -1;
    }
    else
        return -1;
}


